package cn.jiedanba.cacert.common.ca;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.ObjectUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemObject;

import cn.jiedanba.cacert.common.bc.base.BaseSecurity;

public class PkiUtil extends BaseSecurity {

	/**
	 * 获取公钥
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static PublicKey getPublicKey(byte[] encodedKey) {
		try {
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
			KeyFactory keyFactory = KeyFactory.getInstance("X.509", BC);
			PublicKey publicKey = keyFactory.generatePublic(keySpec);
			return publicKey;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * 获取私钥
	 * 
	 * @param encodedKey
	 *            encoded according to the PKCS #8 standard
	 * @return
	 */
	public static PrivateKey getPrivateKey(byte[] encodedKey) {
		PrivateKey privateKey = null;
		try {
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
			KeyFactory keyFactory = KeyFactory.getInstance("X.509", "BC");
			privateKey = keyFactory.generatePrivate(keySpec);
			return privateKey;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}

	}

	/**
	 * 获取ECC公钥
	 * 
	 * @param pubStr
	 * @return
	 * @throws NoSuchProviderException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 * @throws Exception
	 */
	public static ECPublicKey getECPublicKey(byte[] publicKey)
			throws NoSuchAlgorithmException, InvalidKeySpecException {
		byte[] keyBytes = publicKey;
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("EC", BC);
		ECPublicKey pub = (ECPublicKey) keyFactory.generatePublic(keySpec);
		return pub;
	}

	/**
	 * 获取ECC私钥
	 * 
	 * @param priStr
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 * @throws Exception
	 */
	public static ECPrivateKey getECPrivateKey(byte[] privateKey)
			throws NoSuchAlgorithmException, InvalidKeySpecException {
		byte[] keyBytes = privateKey;
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("EC", BC);
		ECPrivateKey pri = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
		return pri;
	}

	/**
	 * 获取签名证书密钥用法
	 * 
	 * @param signCert
	 * @return
	 */
	public static String getKeyUsages(X509Certificate signCert) {
		StringBuffer sb = new StringBuffer();

		// 密钥用法
		boolean[] keyUsages = signCert.getKeyUsage();
		if (keyUsages == null) {
			sb.append("未指定");
			return sb.toString();
		}

		if (keyUsages[0]) {
			sb.append("数字签名,");
		}

		if (keyUsages[1]) {
			sb.append("不可否认,");
		}

		if (keyUsages[2]) {
			sb.append("密钥加密,");
		}

		if (keyUsages[3]) {
			sb.append("数据加密,");
		}

		if (keyUsages[4]) {
			sb.append("密钥协商,");
		}

		if (keyUsages[5]) {
			sb.append("证书签名,");
		}

		if (keyUsages[6]) {
			sb.append("离线CRL签名，CRL签名,");
		}

		if (keyUsages[7]) {
			sb.append("仅加密,");
		}

		if (keyUsages[8]) {
			sb.append("仅解密,");
		}
		return sb.toString().substring(0, sb.toString().length() - 1);
	}

	/**
	 * 获取签名证书扩展用法
	 * 
	 * @param signCert
	 * @return
	 * @throws CertificateParsingException
	 */
	public static String getExtendedKeyUsage(X509Certificate signCert) throws CertificateParsingException {
		StringBuffer sb = new StringBuffer();
		List<String> list = signCert.getExtendedKeyUsage();
		if (ObjectUtils.isEmpty(list)) {
			sb.append("未指定");
			return sb.toString();
		}
		for (String string : list) {
			if (string.equals("2.5.29.37.0")) {
				sb.append("任何扩展密钥用法,");
			} else if (string.equals("1.3.6.1.5.5.7.3.1")) {
				sb.append("服务器身份验证,");
			} else if (string.equals("1.3.6.1.5.5.7.3.2")) {
				sb.append("客户端身份验证,");
			} else if (string.equals("1.3.6.1.5.5.7.3.3")) {
				sb.append("代码签名,");
			} else if (string.equals("1.3.6.1.5.5.7.3.4")) {
				sb.append("安全电子邮件,");
			} else if (string.equals("1.3.6.1.5.5.7.3.5")) {
				sb.append("IP安全终端系统,");
			} else if (string.equals("1.3.6.1.5.5.7.3.6")) {
				sb.append("IP安全隧道终止,");
			} else if (string.equals("1.3.6.1.5.5.7.3.7")) {
				sb.append("IP安全用户,");
			} else if (string.equals("1.3.6.1.5.5.7.3.8")) {
				sb.append("时间戳,");
			} else if (string.equals("1.3.6.1.5.5.7.3.9")) {
				sb.append("OCSP签名,");
			} else if (string.equals("1.3.6.1.4.1.311.20.2.2")) {
				sb.append("智能卡登录,");
			}

		}
		return sb.toString().substring(0, sb.toString().length() - 1);
	}

	/**
	 * 获取公钥证书链
	 * 
	 * @param cer
	 * @return
	 */
	public static Certificate[] getCertificateChain(byte[] cer) {
		try {
			CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509", BC);
			// 获取crt证书的证书链
			Collection<Certificate> chainList = new ArrayList<Certificate>(
					certificatefactory.generateCertificates(new ByteArrayInputStream(cer)));
			return chainList.toArray(new Certificate[] {});
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("获取公钥证书链出错", e);
		}
	}

	/**
	 * 创建CSR证书请求
	 * 
	 * @param dn
	 *            主题
	 * @param signatureAlgorithm
	 *            秘钥算法
	 * @return
	 */
	public static byte[] getPKCS10Certification(String subjectDn, String signatureAlgorithm, KeyPair keyPair) {
		try {
			PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
					new X500Name(subjectDn), keyPair.getPublic());
			JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
			ContentSigner signer = csBuilder.build(keyPair.getPrivate());
			PKCS10CertificationRequest csrRequest = p10Builder.build(signer);
			return csrRequest.getEncoded();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("生成RSA PKCS10出错！！", e);
		}
	}

	/**
	 * 证书链转换为p7b
	 * 
	 * @param chain
	 * @return
	 */
	public static byte[] convertPb7(Certificate[] chain) {
		CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
		try {
			CMSProcessableByteArray msg = new CMSProcessableByteArray("".getBytes());
			JcaCertStore store = new JcaCertStore(Arrays.asList(chain));
			gen.addCertificates(store);
			CMSSignedData signedData = gen.generate(msg);
			return signedData.getEncoded();
		} catch (Exception ex) {
			throw new RuntimeException("Failed to construct P7B response", ex);
		}
	}

	/**
	 * 读取p7b数据
	 * 
	 * @param p7b
	 * @return
	 * @throws CMSException
	 * @throws CertificateException
	 * @throws IOException
	 */
	public static Collection<X509Certificate> readP7b(byte[] p7b) {
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509", BC);
			Collection c = cf.generateCertificates(new ByteArrayInputStream(p7b));
			return c;
		} catch (Exception e) {
			throw new RuntimeException("read p7b data error", e);
		}
	}

	/**
	 * 获取密钥 位数
	 * 
	 * @param pk
	 * @return
	 */
	public static int getKeyLength(final PublicKey pk) {
		if (pk instanceof RSAPublicKey) {
			final RSAPublicKey rsapub = (RSAPublicKey) pk;
			return rsapub.getModulus().bitLength();
		}
		if (pk instanceof JCEECPublicKey) {
			final JCEECPublicKey ecpriv = (JCEECPublicKey) pk;
			final org.bouncycastle.jce.spec.ECParameterSpec spec = ecpriv.getParameters();
			if (spec != null) {
				return spec.getN().bitLength();
			}
			// We support the key, but we don't know the key length
			return 0;
		}
		if (pk instanceof BCECPublicKey) {
			final BCECPublicKey ecpriv = (BCECPublicKey) pk;
			final org.bouncycastle.jce.spec.ECParameterSpec spec = ecpriv.getParameters();
			if (spec != null) {
				return spec.getN().bitLength();
			}
			// We support the key, but we don't know the key length
			return 0;
		}
		if (pk instanceof ECPublicKey) {
			final ECPublicKey ecpriv = (ECPublicKey) pk;
			final java.security.spec.ECParameterSpec spec = ecpriv.getParams();
			if (spec != null) {
				return spec.getOrder().bitLength(); // does this really return
													// something we expect?
			}
			// We support the key, but we don't know the key length
			return 0;
		}
		if (pk instanceof DSAPublicKey) {
			final DSAPublicKey dsapub = (DSAPublicKey) pk;
			if (dsapub.getParams() != null) {
				return dsapub.getParams().getP().bitLength();
			}
			return dsapub.getY().bitLength();
		}
		// Unknown key type
		return -1;
	}

	/**
	 * 证书序列号转字符串
	 * 
	 * @param serialNumber
	 * @return
	 */
	public static String serialNumberConvertString(BigInteger serialNumber) {
		byte[] serial = serialNumber.toByteArray();
		if (serial.length > 0) {
			String serialNumberString = new String();
			for (int i = 0; i < serial.length; i++) {
				String s = Integer.toHexString(Byte.valueOf(serial[i]).intValue());
				if (s.length() == 8)
					s = s.substring(6);
				else if (1 == s.length())
					s = "0" + s;
				serialNumberString += s + "";

			}
			return serialNumberString;
		}
		return null;
	}

	/**
	 * 读取X.509证书
	 * 
	 * @param crtPath
	 *            证书路径
	 * @return
	 * @throws CertificateException
	 * @throws IOException
	 * @throws NoSuchProviderException
	 */
	public static X509Certificate readX509Certificate(byte[] certByte)
			throws CertificateException, IOException, NoSuchProviderException {
		CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
		X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certByte));
		return cert;
	}

	public static String subjectDnSort(String dn) {
		String[] split = dn.split(",");
		String[] result = new String[split.length];
		for (int i = 0, j = split.length - 1; i < split.length; i++, j--) {
			result[j] = split[i];
		}
		StringBuffer sb = new StringBuffer();
		for (String s : result) {
			sb.append(s + ",");
		}
		return sb.toString().substring(0, sb.toString().length() - 1);

	}

	public static String toPemObject(String type, byte[] content) throws Exception {
		PemObject pemCSR = new PemObject(type, content);
		StringWriter str = new StringWriter();
		JcaPEMWriter pemWriter = new JcaPEMWriter(str);
		pemWriter.writeObject(pemCSR);
		pemWriter.close();
		str.close();
		return str.toString();
	}

}
